package com.hfen;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 * @program: websocketnetty
 * @description: 核心业务处理类
 * 接收、处理、响应 客户端WebSocket请求
 * @author: hfen
 * @create: 2021-08-11 15:48
 **/
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    public boolean acceptInboundMessage(Object msg) throws Exception {
        return super.acceptInboundMessage(msg);
    }


    private WebSocketServerHandshaker handshaker;
    public static final String WEB_SOCKET_URL = "ws://localhost:8800/websocket";

    /**
     * 服务端处理客户端websocket的核心方法
     */
    @Override
    public void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {

        /**
         * 一、 处理客户端向服务端发起的握手请求
         *
         * 二、 处理websocket连接业务
         */
        if (msg instanceof FullHttpRequest) {
            handHttpRequest(channelHandlerContext, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handWebSocketFram(channelHandlerContext, (WebSocketFrame) msg);
        }
    }

    public void messageRecived(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {

        /**
         * 一、 处理客户端向服务端发起的握手请求
         *
         * 二、 处理websocket连接业务
         */
        if (msg instanceof FullHttpRequest) {
            handHttpRequest(channelHandlerContext, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handWebSocketFram(channelHandlerContext, (WebSocketFrame) msg);
        }
    }

    /**
     * 处理websocket的连接业务
     * 1. 判断是否为关闭websocket指令，是，关闭连接
     * 2. 判断是否为ping消息, 是，发送通信保持信息
     * 3. 判断是否是二进制消息，是，抛出异常， 不支持二进制消息的
     * 4. 判断是否是文本消息，是，返回应答消息,群发（每个连接上来的客户端）
     */
    private void handWebSocketFram(ChannelHandlerContext channelHandlerContext, WebSocketFrame frame) {
        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(channelHandlerContext.channel(), ((CloseWebSocketFrame) frame).retain());
        } else if (frame instanceof PingWebSocketFrame) {
            channelHandlerContext.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        } else if (frame instanceof BinaryWebSocketFrame) {
            System.out.println("不支持二进制消息");
            throw new RuntimeException(String.format("[%s], 不支持二进制消息 ", this.getClass().getName()));
        } else if (frame instanceof TextWebSocketFrame) {
            // 创建应答信息， 群发消息
            String requestText = ((TextWebSocketFrame) frame).text();
            System.out.println("服务器收到的客户端的消息 ===>>> " + requestText);
            TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(String.format("%s %s ===>>> %s", new Date().toString(), channelHandlerContext.channel().id(), requestText));
            NettyConfig.group.writeAndFlush(textWebSocketFrame);
        }


    }

    /**
     * 处理握手业务
     * 1. 若 response的状态是不成功，或者请求不是websocket请求,发送一个 BAD_REQUEST 回复
     * 2. 若成功且是WebSocket连接， 则握手成功（创建一个握手对象，建立连接）
     * 3. 在WEB_SOCKET_URL的基础上建立握手对象
     */
    private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {

        if (!request.decoderResult().isSuccess() || !"websocket".equals(request.headers().get("Upgrade"))) {
            sendHttpResponse(ctx, request,
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
        } else {
            WebSocketServerHandshakerFactory webSocketServerHandshakerFactory = new WebSocketServerHandshakerFactory(
                    WEB_SOCKET_URL, null, false
            );
            handshaker = webSocketServerHandshakerFactory.newHandshaker(request);
            if (handshaker == null) {
                webSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
            } else {
                handshaker.handshake(ctx.channel(), request);
            }
        }
    }

    /**
     * 发送Http答复信息
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request,
                                  DefaultFullHttpResponse response) {

        if (!response.decoderResult().isSuccess() || "websocket".equals(response.headers().get("Upgrade"))) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
        }
        // 服务端向客户端发送数据
        ChannelFuture channelFuture = ctx.channel().writeAndFlush(response);
        if (response.status().code() != 200) {
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }


    /**
     * 客户端 与 服务端建立连接时调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.add(ctx.channel());
        System.out.println("=== 客户端与服务端连接开启 ===");
    }

    /**
     * 客户端与服务端 断开连接时调用
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.remove(ctx.channel());
        System.out.println("=== 客户端与服务端连接关闭 ===");
    }

    /**
     * 服务端 接收客户端 发送信息结束之后调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    /**
     * 发生异常时调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        // 关闭当前连接
        ctx.close();
    }


}
